--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
meshchatx/src/backend/http_url_guard.py fda8c58d4e0efa93e6527f8312263ef8b7aab5e0 (fda8c58d) Text, 1.58 KB
T8b949e# SPDX-License-Identifier: 0BSD
T8b949e"""Shared HTTP(S) URL validation for outbound client requests (e.g. LibreTranslate)."""
Tff7b72from T7ee787__future__ Tff7b72import Te6edf3annotations
Tff7b72from T7ee787urllibT7ee787.T7ee787parse Tff7b72import Te6edf3urlparseTb4b4b4, Te6edf3urlunparse
Tff7b72class T56d364UnsafeOutboundUrlErrorTb4b4b4(Tf85149ValueErrorTb4b4b4)Tb4b4b4:
T8b949e"""Raised when a URL is not permitted for server-side fetch."""
Tff7b72def Td2a8ffnormalize_loopback_http_service_baseTb4b4b4(Te6edf3urlTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657strTb4b4b4:
T8b949e"""Return scheme://host:port with no path, query, or fragment.
Only ``http``/``https`` to loopback hosts (127.0.0.1, localhost, ::1) are allowed.
Userinfo (embedded credentials) is rejected.
"""
Tff7b72if Tff7b72not Te6edf3url Tff7b72or Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3urlTb4b4b4, Tffa657strTb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must be a non-empty stringTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3parsed Tff7b72= Te6edf3urlparseTb4b4b4(Te6edf3urlTff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72if Te6edf3parsedTff7b72.Td2a8ffscheme Tff7b72not Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ffhttpTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffhttpsTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must use http or httpsTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3netloc Tff7b72= Te6edf3parsedTff7b72.Td2a8ffnetloc Tff7b72or Ta5d6ff"Ta5d6ff"
Tff7b72if Ta5d6ff"Ta5d6ff@Ta5d6ff" Tff7b72in Te6edf3netlocTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must not contain credentialsTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3host Tff7b72= Te6edf3parsedTff7b72.Td2a8ffhostname
Tff7b72if Te6edf3host Tff7b72is Tff7b72NoneTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must include a hostnameTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3host_norm Tff7b72= Te6edf3hostTff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Ta5d6ff"Ta5d6ff[]Ta5d6ff"Tb4b4b4)
Tff7b72if Te6edf3host_norm Tff7b72not Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ff127.0.0.1Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fflocalhostTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff::1Ta5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL host must be 127.0.0.1, localhost, or ::1Ta5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)
T8b949e# Rebuild origin only (LibreTranslate mounts at /languages, /translate, etc.)
Te6edf3authority Tff7b72= Te6edf3netloc
Te6edf3origin Tff7b72= Te6edf3urlunparseTb4b4b4(Tb4b4b4(Te6edf3parsedTff7b72.Td2a8ffschemeTb4b4b4, Te6edf3authorityTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4)
Tff7b72return Te6edf3originTff7b72.Td2a8ffrstripTb4b4b4(Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────